package mornd.test3;

import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

/**
 * @author: mornd
 * @dateTime: 2023/5/5 - 14:12
 */
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        // 根据 beanDefinition 生成 beanName
        AnnotationBeanNameGenerator nameGenerator = new AnnotationBeanNameGenerator();
        try {
            Resource[] resources =
                    resolver.getResources("classpath*:mornd/test3/mapper/**/*.class");
            for (Resource r : resources) {
                CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
                MetadataReader reader = readerFactory.getMetadataReader(r);
                ClassMetadata metadata = reader.getClassMetadata();
                // 只有是mapper包下的接口才会处理，mybatis 也是怎样做的
                if(metadata.isInterface()) {
                    AbstractBeanDefinition beanDefinition =
                            BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(metadata.getClassName())
                            // 设置装配模式通过 byType 来装配的
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();

                    // 这个 bd 就单单为了生成 mapper1 的名称，spring 底层也是如此
                    /**
                     * 如果直接把上面的 beanDefinition 丢进去，生成的名字是 mapperFactoryBean，并且每生成一个mapper接口实现类都会叫
                     * 这个名字，spring的beanName是不能重复的，这样后者就会覆盖前者，导致容器中只会有一个mapper接口实现类
                     */
                    AbstractBeanDefinition generateNameBeanDefinition =
                            BeanDefinitionBuilder.genericBeanDefinition(metadata.getClassName()).getBeanDefinition();

                    String name = nameGenerator.generateBeanName(generateNameBeanDefinition, registry);

                    // 注册 beanDefinition
                    registry.registerBeanDefinition(name, beanDefinition);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
